//
//  ClientThreadController.m
//  Networking
//
//  Created by Christoph Eßer on 11/25/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ClientThreadController.h"
#import <Foundation/Foundation.h>

@implementation ClientThreadController
@synthesize messageDelegate, connID, oldConnID;

-(id) init {
    self = [super init];
    
    if (self) {
        //initialization is done in main thread!
        mainThread = [NSThread currentThread];
        networkingThread = nil;
    }
    
    return self;
}


-(void)dealloc {
    [connection release];	
    [connID release];
    [oldConnID release];
    [super dealloc];
}

-(NSThread*) startNetworkingThreadConnectToAddress: (NSString*) addr onPort: (NSInteger) port {
    if(networkingThread == nil) {
        NSLog(@"starting client networking thread...");
        
        //Only one object can be passed to thread method, so put address and port into dictionary    
        NSValue *portVal = [NSValue valueWithBytes:&port objCType:@encode(int*)];
        NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:addr, @"addr", portVal, @"port",  nil];
        
        networkingThread = [[NSThread alloc] initWithTarget:self selector:@selector(thread:) object:dict];
        [networkingThread start];
        return networkingThread;
    }
    
    else {
        NSLog(@"Error starting client networking thread: Already running!");
        return nil;
    }
}

-(void) stopNetworkingThread {
    if(networkingThread != nil ) {
        [networkingThread cancel];
    }
}

//inData is remote Port listening for connections 
-(void)thread:(id)inData {        
    //No garbage collection here, need autorelease pool!
    NSLog(@"Client thread started");
    
    NSAutoreleasePool*  pool = [[NSAutoreleasePool alloc] init];
    
    //read data from dictionary
    NSDictionary *dict = (NSDictionary*)inData;
    
    //start connection
    NSInteger port;
    [[dict objectForKey:@"port"] getValue:&port];
    NSString* addr = [dict objectForKey:@"addr"];
    
    //NSLog(@"Connecting to server %@ on port %ld", addr, port);
    
    connection = [[Connection alloc] initWithRemoteAddress:addr andPort:port];
    [connection setDelegate: self]; 

    if(![connection noError]) {
        [networkingThread cancel];
        NSLog(@"Error setting up connection");
    }
    else {
        NSLog(@"Connection is up now...");
    }
    
     // run the runloop!!
    while (![networkingThread isCancelled]) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
        
        //if connection is closed unexpectedly, try to reconnect
        if(![connection noError]) {
            NSLog(@"Reconnecting...");
            
            [connection release];
            connection = [[Connection alloc] initWithRemoteAddress:addr andPort:port];
            [connection setDelegate: self]; 
            
            //send reconnect message
            NetworkMessage *msg = [[NetworkMessage alloc] initWithReconnect:oldConnID];
            [self sendMessageToServer:msg];
            [msg release];
        }
        [pool drain]; 
        pool = [[NSAutoreleasePool alloc] init]; // don't forget to drain this at the thread end!!
    }
    
    //Send a disconnect message when the client stop the networking
    //send disconnect message
    NetworkMessage *msg = [[NetworkMessage alloc] initWithDisconnect];
    [self sendMessageToServer:msg];
    [msg release];
    
    NSLog(@"connection now closing...");
    [networkingThread release];
    networkingThread = nil;
    [connection release];
    
    //clean up memory
    [pool drain];
}    

//always use this method to send data to make sure the networking thread is used
-(void) sendMessageToServer: (NetworkMessage*) msg {
    //check first if networking thread is used, otherwise call networking thread manually 
    if(![[NSThread currentThread] isEqual:networkingThread]) {
        [self performSelector:@selector(sendMessageToServer:) onThread:networkingThread withObject:msg waitUntilDone:NO];
    }
    else {  
        [connection sendMessage:msg];
    }
}

//Implement delegate Methods: 
-(void) incomingMessage:(NetworkMessage*) msg fromConnection: (Connection*)conn {
    //handle some messages yourself, others get to delegate...
    switch ([msg type]) {
        case kConnectedMessage:
            connID = [[msg stringMessage] retain];
            break;
            
        default:
            [messageDelegate performSelector:@selector(incomingMessage:) onThread:mainThread withObject:msg waitUntilDone:NO];
            break;
    }
}

-(void) connectionAborted:(Connection *)conn {
    NSLog(@"CLIENT: Connection to server aborted");
    oldConnID = [connID retain];
    [connection setNoError:FALSE];
}
@end
